package com.kdgc.energy.dmm.result;

import cn.hutool.core.util.StrUtil;
import com.kdgc.energy.dmm.Environment;
import com.kdgc.energy.dmm.table.Table;
import org.apache.metamodel.data.DataSet;
import org.apache.metamodel.data.Row;
import org.apache.metamodel.query.SelectItem;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author xu.wenchang
 * @version 1.0 2022/04/24
 */
public abstract class AbstractResult {
    protected Map<String, Field> buildFieldMap(Table table) {
        Class<?> entityClass = table.getEntityInfo()
                                    .getEntityClass();
        Field[] fields = entityClass.getDeclaredFields();
        return Arrays.stream(fields)
                     .filter(f -> !Modifier.isStatic(f.getModifiers()))
                     .collect(Collectors.toMap(field -> table.getColumnByField(field.getName())
                                                             .getName(), field -> field));
    }
    
    protected List<Table> getTableInfo(DataSet dataSet) {
        List<SelectItem> selectItems = dataSet.getSelectItems();
        
        boolean hasFunctionColumn = selectItems.stream()
                                               .map(SelectItem::getColumn)
                                               .anyMatch(Objects::isNull);
        
        if (hasFunctionColumn) {
            return null;
        }
        
        List<Table> tableSet = selectItems.stream()
                                          .map(si -> Environment.findByTableName(si.getColumn()
                                                                                   .getTable()
                                                                                   .getName()))
                                          .distinct()
                                          .collect(Collectors.toList());
        
        if (tableSet.size() == 0) {
            throw new RuntimeException("缺少实体类信息");
        }
        
        return tableSet;
    }
    
    protected Object fillObject(Class<?> clazz, Row row, Map<String, Field> fieldMap) throws Exception {
        Object inst = clazz.newInstance();
        
        for (SelectItem si : row.getSelectItems()) {
            String key = StrUtil.isNotBlank(si.getAlias()) ? si.getAlias() : si.getColumn()
                                                                               .getName();
            
            Field f = fieldMap.get(key);
            
            if (null == f && null != si.getColumn()) {
                Table table = Environment.findByTableName(si.getColumn()
                                                            .getTable()
                                                            .getName());
                String fieldName = table.getFieldByColumn(si.getColumn()
                                                            .getName());
                f = fieldMap.get(fieldName);
            }
            
            if (null == f) {
                continue;
            }
            
            f.setAccessible(true);
            setValue(inst, f, row.getValue(si));
        }
        
        return inst;
    }
    
    private void setValue(Object inst, Field f, Object value) throws Exception {
        if (null == value) {
            return;
        }
        
        String toStringValue = value.toString();
        
        switch (f.getType()
                 .getName()) {
            case "long":
            case "java.lang.Long": {
                f.set(inst, new Long(toStringValue));
                return;
            }
            case "int":
            case "java.lang.Integer": {
                f.set(inst, new Integer(toStringValue));
                return;
            }
            case "short":
            case "java.lang.Short": {
                f.set(inst, new Short(toStringValue));
                return;
            }
            case "byte":
            case "java.lang.Byte": {
                f.set(inst, new Byte(toStringValue));
                return;
            }
            case "float":
            case "java.lang.Float": {
                f.set(inst, new Float(toStringValue));
                return;
            }
            case "double":
            case "java.lang.Double": {
                f.set(inst, new Double(toStringValue));
                return;
            }
            case "java.math.BigInteger": {
                f.set(inst, new BigInteger(toStringValue));
                return;
            }
            case "java.math.BigDecimal": {
                f.set(inst, new BigDecimal(toStringValue));
                return;
            }
            case "char":
            case "java.lang.Character": {
                f.set(inst, toStringValue.charAt(0));
                return;
            }
            case "boolean":
            case "java.lang.Boolean": {
                f.set(inst, Boolean.valueOf(toStringValue));
                return;
            }
            default:
                f.set(inst, value);
        }
    }
}
